package ui

import (
	"SocketTest/event"
	"SocketTest/socket"
	"fmt"
	"gioui.org/app"
	"gioui.org/font/gofont"
	"gioui.org/layout"
	"gioui.org/text"
	"gioui.org/unit"
	"gioui.org/widget"
	"gioui.org/widget/material"
	"gioui.org/x/component"
	"golang.org/x/text/encoding/simplifiedchinese"
	"golang.org/x/text/transform"
	"image/color"
	"log"
	"os"
	"time"
)

type SocketTestUI struct {
	window                   *app.Window
	th                       *material.Theme
	host, port               component.TextField
	req                      *material.EditorStyle
	resp                     *ResponseEdit
	sendFile, send, download *Button
	protocol                 *RadioButtonGroup
	sendCharset              *RadioButtonGroup
	recvCharset              *RadioButtonGroup

	dialog *Dialog
}

var (
	hSpacer = layout.Rigid(layout.Spacer{Width: unit.Dp(10)}.Layout)
	vSpacer = layout.Rigid(layout.Spacer{Height: unit.Dp(10)}.Layout)
	BUS     = event.NewEventBus()
)

func (ui *SocketTestUI) Layout(gtx layout.Context) layout.Dimensions {
	mainUI := layout.UniformInset(unit.Dp(8)).Layout(gtx, func(gtx layout.Context) layout.Dimensions {
		return layout.Flex{Axis: layout.Vertical}.Layout(gtx,
			layout.Flexed(1, func(gtx layout.Context) layout.Dimensions {
				return ui.SendLayout(gtx)
			}),
			vSpacer,
			layout.Flexed(1, func(gtx layout.Context) layout.Dimensions {
				return ui.ReceiveLayout(gtx)
			}),
			vSpacer,
		)
	})

	return layout.Stack{}.Layout(gtx,
		layout.Expanded(func(gtx layout.Context) layout.Dimensions {
			return mainUI
		}),
		layout.Stacked(func(gtx layout.Context) layout.Dimensions {
			return ui.dialog.Layout(gtx)
		}),
	)
}

func (ui *SocketTestUI) SendLayout(gtx layout.Context) layout.Dimensions {
	return layout.Flex{Axis: layout.Vertical}.Layout(gtx,
		// host port
		layout.Rigid(func(gtx layout.Context) layout.Dimensions {
			return layout.Flex{Axis: layout.Horizontal}.Layout(gtx,
				layout.Flexed(3, func(gtx layout.Context) layout.Dimensions {
					return ui.host.Layout(gtx, ui.th, "Host")
				}),
				hSpacer,
				layout.Flexed(1, func(gtx layout.Context) layout.Dimensions {
					return ui.port.Layout(gtx, ui.th, "Port")
				}),
			)
		}),
		vSpacer,
		// protocol charset send
		layout.Rigid(func(gtx layout.Context) layout.Dimensions {
			return layout.Flex{Axis: layout.Horizontal}.Layout(gtx,
				// protocol
				layout.Flexed(1, func(gtx layout.Context) layout.Dimensions {
					return ui.protocol.Layout(gtx)
				}),
				hSpacer,
				// charset
				layout.Flexed(1, func(gtx layout.Context) layout.Dimensions {
					return ui.sendCharset.Layout(gtx)
				}),
				hSpacer,
				// send file
				layout.Rigid(func(gtx layout.Context) layout.Dimensions {
					return ui.sendFile.Layout(gtx)
				}),
				hSpacer,
				// send
				layout.Rigid(func(gtx layout.Context) layout.Dimensions {
					return ui.send.Layout(gtx)
				}),
			)
		}),
		vSpacer,
		layout.Flexed(1, func(gtx layout.Context) layout.Dimensions {
			return widget.Border{Width: unit.Dp(1), Color: color.NRGBA{A: 100}, CornerRadius: unit.Dp(5)}.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
				return layout.UniformInset(unit.Dp(5)).Layout(gtx, func(gtx layout.Context) layout.Dimensions {
					return ui.req.Layout(gtx)
				})
			})
		}),
	)
}

func (ui *SocketTestUI) ReceiveLayout(gtx layout.Context) layout.Dimensions {
	border := widget.Border{Width: unit.Dp(1), Color: color.NRGBA{A: 100}}
	return layout.Flex{Axis: layout.Vertical}.Layout(gtx,
		layout.Rigid(func(gtx layout.Context) layout.Dimensions {
			return layout.Flex{Axis: layout.Horizontal}.Layout(gtx,
				layout.Flexed(1, func(gtx layout.Context) layout.Dimensions {
					return ui.recvCharset.Layout(gtx)
				}),
				hSpacer,
				layout.Rigid(func(gtx layout.Context) layout.Dimensions {
					return ui.download.Layout(gtx)
				}),
			)
		}),
		vSpacer,
		layout.Flexed(1, func(gtx layout.Context) layout.Dimensions {
			return border.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
				return layout.UniformInset(unit.Dp(5)).Layout(gtx, func(gtx layout.Context) layout.Dimensions {
					return ui.resp.Layout(gtx)
				})
			})
		}),
	)
}

func NewSocketTestUI(w *app.Window) *SocketTestUI {
	th := material.NewTheme()

	reqEditor := new(widget.Editor)
	req := material.Editor(th, reqEditor, "Request Here...")

	ui := &SocketTestUI{
		window:      w,
		th:          th,
		req:         &req,
		protocol:    NewRadioButtonGroup(th, "Protocol", []string{"tcp", "udp"}, "tcp"),
		sendCharset: NewRadioButtonGroup(th, "SendCharset", []string{"utf8", "gbk"}, "utf8"),
		recvCharset: NewRadioButtonGroup(th, "RecvCharset", []string{"utf8", "gbk"}, "utf8"),

		dialog: NewDialog(th),
	}

	ui.sendFile = NewButton(th, "SendFile", "SendFile")
	ui.send = NewButton(th, "Send", "Send")
	ui.download = NewButton(th, "Download", "Download")

	ui.resp = NewResponseEdit(th, "Response Here...")

	ui.th.Shaper = text.NewShaper(text.WithCollection(gofont.Collection()))

	return ui
}

func (ui *SocketTestUI) InitEventHandlers() {
	refreshUICh := BUS.Subscribe("RefreshUI")
	go func() {
		for range refreshUICh {
			ui.window.Invalidate()
		}
	}()

	sendCh := BUS.Subscribe("Send")
	go func() {
		for range sendCh {
			t := ui.req.Editor.Text()

			charset := ui.sendCharset.GetSelectedItem()

			var buf []byte
			if charset == "gbk" {
				encoder := simplifiedchinese.GBK.NewEncoder()
				result, _, err := transform.Bytes(encoder, []byte(t))
				if err != nil {
					log.Println(err)
					ui.dialog.ShowError("错误", err.Error())
					continue
				} else {
					buf = result
				}
			} else {
				buf = []byte(t)
			}

			ui.dialog.ShowInfo("提示", "操作开始...")
			ui.dialog.ConfirmVisible = false
			result, err := socket.SendMessage(ui.protocol.GetSelectedItem(), fmt.Sprintf("%s:%s", ui.host.Text(), ui.port.Text()), buf, 3*time.Minute)
			ui.dialog.Message = "操作完成"
			ui.dialog.ConfirmVisible = true
			if err != nil {
				log.Println(err)
				ui.dialog.ShowError("错误", err.Error())
			} else {
				ui.resp.SetContent(result)
			}
		}
	}()

	recvCharsetCh := BUS.Subscribe("RecvCharset")
	go func() {
		for obj := range recvCharsetCh {
			radioBtn := obj.(RadioButtonGroup)
			ui.resp.SetCharset(radioBtn.curValue)
			ui.window.Invalidate()
		}
	}()

	downloadCh := BUS.Subscribe("Download")
	go func() {
		for range downloadCh {
			filename := fmt.Sprintf("socket_test_%s.bin", time.Now().Format("20060102150405"))
			err := os.WriteFile(filename, ui.resp.Content(), 0666)
			if err != nil {
				log.Println(err)
				ui.dialog.ShowError("错误", err.Error())
			} else {
				ui.dialog.ShowInfo("信息", "文件已写出："+filename)
			}
		}
	}()
}
